/* Copyright (c) 2010, Peter Lényi */
/* Licensed under The BSD License. */

package gem.apps.fs.engine;

import gem.apps.fs.db.DatabaseCallException;
import gem.apps.fs.db.DatabaseManager;
import gem.apps.fs.db.DatabaseManagerImpl;
import gem.apps.fs.db.FileManager;
import gem.apps.fs.db.FileManagerImpl;
import gem.apps.fs.db.FileStorageException;
import gem.apps.fs.entities.FileMeta;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Date;
import java.util.List;
import org.apache.commons.fileupload.FileItem;

/**
 * Manager class FileServer provides methods for primary application's functionality.
 * 
 * @author Peter Lényi
 * @version 20100426F
 */
public class FileServer
{
    private static FileServer instance = null;

    private static final int PAGE = 20;

    private DatabaseManager dman = DatabaseManagerImpl.getInstance();
    private FileManager fman = FileManagerImpl.getInstance();

    private FileServer() { /* SINGLETON, FACTORY METHOD PATTERNS */ }

    /**
     * @return class instance.
     */
    public static FileServer getInstance() {
        if (instance == null) {
            instance = new FileServer();
        }
        return instance;
    }

    /**
     * Upload a file to the file server.
     * @param file
     *          file to be uploaded.
     * @param title
     *          name of the file as displayed in the application.
     * @param comment
     *          owner's comment.
     * @param owner
     *          owner's name.
     * @param password
     *          if specified, required to view file details and download the file.
     * @param mime
     *          mime type of the file to improve downloading.
     * @return uin of the uploaded file, or -1 if the upload failed
     */
    public long uploadFile(FileItem file, String title, String comment, String owner, String password, String mime) {
        long result = -1;
        FileMeta meta = new FileMeta();
        try {
            String originalName = file.getName();
            int lis = originalName.lastIndexOf("/");
            int libs = originalName.lastIndexOf("\\");
            lis = libs > lis ? libs : lis;
            originalName = originalName.substring(lis + 1);
            meta.setOriginalName(originalName);
            meta.setTitle(title);
            meta.setUploaded(new Date(new java.util.Date().getTime()));
            meta.setComment(comment);
            meta.setOwner(owner);
            meta.setPassword(password);
            meta.setMime(mime);
            dman.insertFileMeta(meta);
            fman.storeFile(new File(Long.toString(meta.getUin())), file.getInputStream());
            result = meta.getUin();
        }
        catch (Exception ex) {}
        return result;
    }

    /**
     * Retrieve list of all file meta data records stored by file server.
     * @return list of all file meta data records.
     */
    public List<FileMeta> listFiles() {
        List<FileMeta> result = null;
        try {
            result = dman.listFileMeta();
        }
        catch (DatabaseCallException ex) {}
        return result;
    }

    /**
     * Retrieve subset of all file meta data records stored by file server.
     * @return subset of all file meta data records.
     * @param pageNumber
     *          for paged list, this is number of displayed page.
     */
    public List<FileMeta> listFiles(int pageNumber) {
        List<FileMeta> result = null;
        try {
            result = dman.listFileMeta(PAGE, pageNumber);
        }
        catch (DatabaseCallException ex) {}
        return result;
    }

    /**
     * Get all meta data for specific file.
     * @param uin
     *          file's unique identification number assigned by the system.
     * @return file meta data.
     */
    public FileMeta getFileMeta(long uin) {
        FileMeta result = new FileMeta();
        result.setUin(uin);
        try {
            result = dman.selectFileMeta(result);
        }
        catch (DatabaseCallException ex) {
            result = null;
        }
        return result;
    }

    /**
     * Retrieve a specific file to be sent to browser.
     * @param uin
     *          file's unique identification number assigned by the system.
     * @return representation of file in local filesystem.
     */
    public FileInputStream loadFile(long uin) {
        FileInputStream result = null;
        try {
            if (fman.exists(uin) && fman.isFile(uin)) {
                result = fman.loadFile(uin);
            }
        }
        catch (FileStorageException ex) {}
        return result;
    }

    /**
     * Remove a specific file.
     * @param uin
     *          file's unique identification number assigned by the system.
     * @return true if the file was deleted, false otherwise.
     */
    public boolean deleteFile(long uin) {
        boolean result = false;
        try {
            if (fman.exists(uin) && fman.isFile(uin)) {
                fman.deleteFile(uin);
            }
            try {
                FileMeta meta = new FileMeta();
                meta.setUin(uin);
                dman.deleteFileMeta(meta);
                result = true;
            }
            catch (DatabaseCallException ex) {}
        }
        catch (FileStorageException ex) {}
        return result;
    }

    /**
     * Update a specific file.
     * @param uin
     *          file's unique identification number assigned by the system.
     * @param title
     *          new title.
     * @param comment
     *          new comment.
     * @param password
     *          new password.
     * @param mime
     *          new MIME type.
     * @return true if the file metadata has been updated; or false otherwise.
     */
    public boolean updateFile(long uin, String title, String comment, String password, String mime) {
        boolean result = false;
        try {
            FileMeta meta = new FileMeta();
            meta.setUin(uin);
            meta.setTitle(title);
            meta.setComment(comment);
            meta.setPassword(password);
            meta.setMime(mime);
            dman.updateFileMeta(meta);
            result = true;
        }
        catch (DatabaseCallException ex) {}
        return result;
    }
}
